%%%-------------------------------------------------------------------
%% @doc crontab_timer public API
%% @end
%%%-------------------------------------------------------------------

-module(crontab_timer).
-behaviour(gen_server).


%% --------------------------------------------------------------------
%% External exports
%% --------------------------------------------------------------------
-export([start_link/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).


-record(state, { restart
               , date
               , time
               , week
               , minute
               }).
%% ====================================================================
%% External functions
%% ====================================================================
start_link(_) ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).


%% ====================================================================
init([]) ->
    State = init_state(),
    Timer = next_minute_remaining_millisecond(State#state.minute),
    timer:send_after(Timer, crontab_trigger),
    {ok, State}.

%% --------------------------------------------------------------------
handle_call(_Info, _From, State) ->
    {reply, ok, State}.

%% --------------------------------------------------------------------
handle_cast(_Info, State) ->
    {noreply, State}.

%% --------------------------------------------------------------------
handle_info(crontab_trigger, State) ->
    Minute = State#state.minute + 1,
    NewState = 
    if
        Minute >= 1440 -> init_state();
        true -> State#state{ time = calendar:seconds_to_time(Minute * 60)
                           , minute = Minute}
    end,
    Timer = next_minute_remaining_millisecond(NewState#state.minute),
    timer:send_after(Timer, crontab_trigger),

    gen_server:cast(crontab_trigger, { timercall
                                     , NewState#state.date
                                     , NewState#state.time
                                     , NewState#state.week
                                     }),

    % io:format("-- now: ~p, trigger: ~p ~p ~p, send_after: ~p", 
    %     [ calendar:local_time()
    %     , NewState#state.date
    %     , NewState#state.time
    %     , NewState#state.week
    %     , Timer
    %     ]),
    {noreply, NewState};

handle_info(_Info, State) ->
    {noreply, State}.

%% --------------------------------------------------------------------
terminate(_Reason, _Status) ->
    ok.

%% --------------------------------------------------------------------
code_change(_oldvsn, Status, _extra) ->
    {ok, Status}.


%% internal functions
%% --------------------------------------------------------------------
init_state() ->
    {Date, Time} = calendar:local_time(),
    #state{ restart = {Date, Time}
          , date = Date
          , time = Time
          , week = calendar:day_of_the_week(Date)
          , minute = calendar:time_to_seconds(Time) div 60
          }.


%% the remaining seconds until the next minute
next_minute_remaining_millisecond(Minute) ->
    {_, Time} = calendar:local_time(),
    (Minute * 60 + 61 - calendar:time_to_seconds(Time)) * 1000.

